﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace FoundationLib.Web.Mvc.DataAnnotation
{
	/// <summary>
	/// An attribute which can be used to annotate MVC model properties so that a particular CSS style is 
	/// applied if their value is in a range defined by a minimum and maximum value.
	/// </summary>
	[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
	public class RangeStyleAttribute : Attribute
	{
		/// <summary>
		/// The minimum value
		/// </summary>
		private double min;
		/// <summary>
		/// The maximum value
		/// </summary>
		private double max;
		/// <summary>
		/// The CSS style to be applied if the value is in the range
		/// </summary>
		private string style;


		/// <summary>
		/// Creates a new <see cref="RangeStyleAttribute"/> instance.
		/// </summary>
		/// <param name="min">The minimum value</param>
		/// <param name="max">The maximum value</param>
		/// <param name="style">The CSS style to be applied if the value is in the range</param>
		public RangeStyleAttribute(double min, double max, string style)
		{
			Contract.Requires(min <= max, "Min must be less or equal to max.");
			this.min = min;
			this.max = max;
			this.style = style;
		}

		/// <summary>
		/// Creates a new <see cref="RangeStyleAttribute"/> instance.
		/// </summary>
		/// <param name="min">The minimum value</param>
		/// <param name="max">The maximum value</param>
		/// <param name="style">The CSS style to be applied if the value is in the range</param>
		public RangeStyleAttribute(int min, int max, string style)
			: this((double)min, (double)max, style)
		{
		}

		/// <summary>
		/// Creates a new <see cref="RangeStyleAttribute"/> instance.
		/// </summary>
		/// <param name="min">The minimum value</param>
		/// <param name="max">The maximum value</param>
		/// <param name="style">The CSS style to be applied if the value is in the range</param>
		public RangeStyleAttribute(float min, float max, string style)
			: this((double)min, (double)max, style)
		{
		}

		/// <summary>
		/// Gets the minimum value.
		/// </summary>
		public double Min
		{
			get
			{
				return this.min;
			}
		}

		/// <summary>
		/// Gets the maximum value.
		/// </summary>
		public double Max
		{
			get
			{
				return this.max;
			}
		}

		/// <summary>
		/// Gets the CSS style.
		/// </summary>
		public string Style
		{
			get
			{
				return this.style;
			}
		}

		/// <summary>
		/// Tests whether the specified value falls into the range defined by this range style 
		/// attribute and returns the style to be applied.
		/// </summary>
		/// <param name="value">The value to test</param>
		/// <param name="style">The CSS style if <paramref name="value"/> is in the range, 
		/// or null if the value is out of range</param>
		/// <returns>True if the value is in the range, in which case <paramref name="style"/> should hold the 
		/// corresponding style as defined by this attribute, or false otherwise</returns>
		internal bool Test(object value, out string style)
		{
			style = null;

			if (value == null)
				return false;

			string stringValue = value.ToString();

			double numericValue = 0.0;
			if (!Double.TryParse(stringValue, out numericValue))
			{
				return false;
			}

			if (numericValue >= this.min && numericValue < this.max)
			{
				style = this.style;
				return true;
			}
			else
			{
				return false;
			}
		}
	}
}
